{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Deploy Resnest model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 1. Download Resnet model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "mkdir /tmp/resnet"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x 1538687457/\n",
      "x 1538687457/variables/\n",
      "x 1538687457/variables/variables.index\n",
      "x 1538687457/variables/variables.data-00000-of-00001\n",
      "x 1538687457/saved_model.pb\n"
     ]
    }
   ],
   "source": [
    "!curl -s https://storage.googleapis.com/download.tensorflow.org/models/official/20181001_resnet/savedmodels/resnet_v2_fp32_savedmodel_NHWC_jpg.tar.gz | tar --strip-components=2 -C /tmp/resnet -xvz"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[34m1538687457\u001b[m\u001b[m/\r\n"
     ]
    }
   ],
   "source": [
    "ls /tmp/resnet/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'/Users/macbook/Desktop/learn-machine-learning-in-two-months/deployment/distributed-tensorflow'"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pwd"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[34m1538687457\u001b[m\u001b[m/\r\n"
     ]
    }
   ],
   "source": [
    "ls /tmp/resnet"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2. Kéo TF Serving Model về"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Using default tag: latest\n",
      "latest: Pulling from tensorflow/serving\n",
      "\n",
      "\u001b[1B39215d1d: Pulling fs layer \n",
      "\u001b[1B88e9f672: Pulling fs layer \n",
      "\u001b[1B3b1caad4: Pulling fs layer \n",
      "\u001b[1Ba26005b7: Pulling fs layer \n",
      "\u001b[1B76a48494: Pulling fs layer \n",
      "\u001b[1B79dc1d76: Pulling fs layer \n",
      "\u001b[1Bcc9ce8fa: Pulling fs layer \n",
      "\u001b[1BDigest: sha256:f7e59a29cbc17a6b507751cddde37bccad4407c05ebf2c13b8e6ccb7d2e9affbA\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[5A\u001b[2K\u001b[4A\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[4A\u001b[2K\u001b[4A\u001b[2K\u001b[4A\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[2A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[1A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[3A\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[8A\u001b[2K\u001b[7A\u001b[2K\u001b[7A\u001b[2K\u001b[6A\u001b[2K\u001b[5A\u001b[2K\u001b[4A\u001b[2K\u001b[4A\u001b[2K\u001b[4A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[3A\u001b[2K\u001b[2A\u001b[2K\u001b[1A\u001b[2K\n",
      "Status: Downloaded newer image for tensorflow/serving:latest\n",
      "docker.io/tensorflow/serving:latest\n"
     ]
    }
   ],
   "source": [
    "!docker pull tensorflow/serving"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 3. Deploy lên Docker"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Deploy và chạy API trên cổng **8501**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2019-08-05 09:27:16.235457: I tensorflow_serving/model_servers/server.cc:82] Building single TensorFlow model file config:  model_name: resnet model_base_path: /models/resnet\n",
      "2019-08-05 09:27:16.235812: I tensorflow_serving/model_servers/server_core.cc:462] Adding/updating models.\n",
      "2019-08-05 09:27:16.235875: I tensorflow_serving/model_servers/server_core.cc:561]  (Re-)adding model: resnet\n",
      "2019-08-05 09:27:16.348780: I tensorflow_serving/core/basic_manager.cc:739] Successfully reserved resources to load servable {name: resnet version: 1538687457}\n",
      "2019-08-05 09:27:16.348838: I tensorflow_serving/core/loader_harness.cc:66] Approving load for servable version {name: resnet version: 1538687457}\n",
      "2019-08-05 09:27:16.348857: I tensorflow_serving/core/loader_harness.cc:74] Loading servable version {name: resnet version: 1538687457}\n",
      "2019-08-05 09:27:16.349102: I external/org_tensorflow/tensorflow/contrib/session_bundle/bundle_shim.cc:363] Attempting to load native SavedModelBundle in bundle-shim from: /models/resnet/1538687457\n",
      "2019-08-05 09:27:16.349155: I external/org_tensorflow/tensorflow/cc/saved_model/reader.cc:31] Reading SavedModel from: /models/resnet/1538687457\n",
      "2019-08-05 09:27:16.362696: I external/org_tensorflow/tensorflow/cc/saved_model/reader.cc:54] Reading meta graph with tags { serve }\n",
      "2019-08-05 09:27:16.419778: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:202] Restoring SavedModel bundle.\n",
      "2019-08-05 09:27:16.963875: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:151] Running initialization op on SavedModel bundle at path: /models/resnet/1538687457\n",
      "2019-08-05 09:27:17.009313: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:311] SavedModel load for tags { serve }; Status: success. Took 660148 microseconds.\n",
      "2019-08-05 09:27:17.010169: I tensorflow_serving/servables/tensorflow/saved_model_warmup.cc:103] No warmup data file found at /models/resnet/1538687457/assets.extra/tf_serving_warmup_requests\n",
      "2019-08-05 09:27:17.018780: I tensorflow_serving/core/loader_harness.cc:86] Successfully loaded servable version {name: resnet version: 1538687457}\n",
      "2019-08-05 09:27:17.021563: I tensorflow_serving/model_servers/server.cc:324] Running gRPC ModelServer at 0.0.0.0:8500 ...\n",
      "[warn] getaddrinfo: address family for nodename not supported\n",
      "[evhttp_server.cc : 239] RAW: Entering the event loop ...\n",
      "2019-08-05 09:27:17.023302: I tensorflow_serving/model_servers/server.cc:344] Exporting HTTP/REST API at:localhost:8501 ...\n",
      "^C\n"
     ]
    }
   ],
   "source": [
    "!docker run -p 8501:8501 --name tfserving_resnet \\\n",
    "--mount type=bind,source=/tmp/resnet,target=/models/resnet \\\n",
    "-e MODEL_NAME=resnet -t tensorflow/serving"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Giải thích dòng lệnh:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- **-p 8501:8501** : Cổng mà bạn serve API\n",
    "- **--name** : Tên của container\n",
    "- **--mount type=bind,source=/tmp/resnet,target=/models/resnet** : Nối docker container với thư mục đang chứa model\n",
    "- **-e MODEL_NAME=resnet** : Tên của model\n",
    "- **-t tensorflow/serving** : Chạy container trên image tensorflow/serving"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 4. Viết hàm gọi API"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "from __future__ import print_function\n",
    "\n",
    "import base64\n",
    "import requests\n",
    "\n",
    "# The server URL specifies the endpoint of your server running the ResNet\n",
    "# model with the name \"resnet\" and using the predict interface.\n",
    "SERVER_URL = 'http://localhost:8501/v1/models/resnet:predict'\n",
    "\n",
    "# The image URL is the location of the image we should send to the server\n",
    "IMAGE_URL = 'https://tensorflow.org/images/blogs/serving/cat.jpg'\n",
    "\n",
    "def call_api():\n",
    "  # Download the image\n",
    "  dl_request = requests.get(IMAGE_URL, stream=True)\n",
    "  dl_request.raise_for_status()\n",
    "\n",
    "  # Compose a JSON Predict request (send JPEG image in base64).\n",
    "  jpeg_bytes = base64.b64encode(dl_request.content).decode('utf-8')\n",
    "  predict_request = '{\"instances\" : [{\"b64\": \"%s\"}]}' % jpeg_bytes\n",
    "\n",
    "  # Send few requests to warm-up the model.\n",
    "  for _ in range(3):\n",
    "    response = requests.post(SERVER_URL, data=predict_request)\n",
    "    response.raise_for_status()\n",
    "\n",
    "  # Send few actual requests and report average latency.\n",
    "  total_time = 0\n",
    "  num_requests = 10\n",
    "  for _ in range(num_requests):\n",
    "    response = requests.post(SERVER_URL, data=predict_request)\n",
    "    response.raise_for_status()\n",
    "    total_time += response.elapsed.total_seconds()\n",
    "    prediction = response.json()['predictions'][0]\n",
    "\n",
    "  print('Prediction class: {}, avg latency: {} ms'.format(\n",
    "      prediction['classes'], (total_time*1000)/num_requests))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Predicting...\n",
      "Prediction class: 286, avg latency: 130.5545 ms\n",
      "Done...\n"
     ]
    }
   ],
   "source": [
    "print(\"Predicting...\")\n",
    "call_api()\n",
    "print(\"Done...\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 5. Đối chiếu với nhãn"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Theo danh sách 1000 nhãn được train thì nhãn này là **cougar, puma, catamount, mountain lion, painter, panther, Felis concolor',**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[Danh sách nhãn](https://gist.github.com/yrevar/942d3a0ac09ec9e5eb3a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
